"""
策略基类模块
定义策略开发的基本接口和通用功能
"""
from abc import ABC, abstractmethod
from datetime import datetime
from typing import Dict, List, Optional
import pandas as pd
from loguru import logger

from engine.position import  PortfolioPosition
from engine.assets import Asset
from engine.object import BarData, OrderData,TradeData,CashFlowData,BaseData
from engine.constant import OrderType
def virtual(func:callable):
    return func
class Strategy(ABC):
    """策略基类"""
    
    def __init__(self, 
                 name: str,
                 portfolio_flag: bool ,
                 initial_capital: float = 1000000.0,
                 risk_limit: float = 0.1,
                 leverage: float = 1.0,
                 **kwargs,
                 ):
        """
        初始化策略
        
        参数:
            name: 策略名称
            initial_capital: 初始资金
            risk_limit: 风险限额，以总资产比例表示
        """
        self.name = name
        self.initial_capital = initial_capital
        self.risk_limit = risk_limit
        self.leverage = leverage
        
        # 市场数据
        self.market_data: Dict[str, pd.DataFrame] = {}
        self.current_date: Optional[datetime] = None
        
        # 持仓管理
        self.portfolio = PortfolioPosition(initial_capital=initial_capital)
        self.available_assets: Dict[str, Asset] = {}
        
        # 订单管理
        self.pending_orders: List[OrderData] = []
        self.engine = None
        self.portfolio_flag = portfolio_flag
        
        
        logger.info(f"策略 {name} 初始化完成")
    
 
    
    def get_current_price(self, symbol: str,price_type='net') -> float:
        """
        获取当前价格
        
        参数:
            symbol: 证券代码
            price_type: 价格类型，'net'表示净价，'full'表示全价
        返回:
            当前价格
        """
        if symbol not in self.market_data:
            raise ValueError(f"未找到 {symbol} 的市场数据")
        
        # 从BarData字典中获取当前日期的数据
        if self.current_date not in self.market_data[symbol]:
            available_dates = list(self.market_data[symbol].keys())
            logger.warning(f"未找到 {symbol} 在 {self.current_date} 的市场数据。可用日期: {available_dates[:5]}...")
            # 如果没有找到数据则返回最新可取到的市场数据
            bar_data = self.market_data[symbol][available_dates[-1]]
        else:
        # 从BarData对象获取价格
            bar_data = self.market_data[symbol][self.current_date]
        clean_price = bar_data.close_price
        
        if price_type == 'net':
            res = clean_price
        elif price_type == 'full':
            accured_interest = self.available_assets[symbol].accrued_interest(self.current_date, 1)
            res = clean_price + accured_interest
        
        return res
    

    
 
    
    def adjust_target_position(self, target_weights: Dict[str, float], tolerance: float = 0.001) -> None:
        """
        根据目标权重调整持仓
        
        Args:
            target_weights (Dict[str, float]): 目标持仓权重，例如 {'bond_1': 0.3, 'bond_2': 0.7}
            tolerance (float, optional): 持仓调整的容忍度。如果当前权重与目标权重的差异
                                      小于此值则不调整。默认为0.03（3%）
        """
        # 获取当前总资产


        total_value = self.portfolio.net_total_value() * self.leverage

        # 获取当前所有持仓的标的和权重
        current_positions = self.portfolio.positions
        current_weights = {}
        
        # 计算当前持仓权重
        for symbol, position in current_positions.items():
            if position.quantity > 0:
                current_fullprice = self.get_current_price(symbol,'full')
                if current_fullprice is None:
                    # 如果取不到价格则按照之前的价格计算
                    current_fullprice = self.portfolio.positions.get(symbol).current_price
                else:
                    current_value = position.quantity * current_fullprice
                    current_weights[symbol] = current_value / total_value

        # 记录日志
        logger.info("目标仓位")
        logger.info(f"目标权重: {target_weights}")
        logger.info(f"当前权重: {current_weights}")
        cashadd = 0
        # 处理需要清仓的标的（在当前持仓中但不在目标持仓中）
        for symbol in current_weights.keys():
            if symbol not in target_weights:
                # 获取当前持仓数量
                current_quantity = current_positions[symbol].quantity
                if current_quantity > 0:
                    # 发出卖出订单
                    self.place_order(
                        symbol=symbol,
                        order_type=OrderType.MARKET,
                        quantity=current_quantity,
                        direction=-1  # 卖出
                    )
                # 卖出后持仓市值会降低,现金增加
                cashadd += current_positions[symbol].market_value()
                


        # 调整目标持仓中的标的
        # 如果有任意一个标的的差异大于容忍度，则对所有标的进行rebalance；否则所有标的都不进行调整
        rebalance_flag = False
        for symbol, target_weight in target_weights.items():
            current_weight = current_weights.get(symbol, 0.0)
            
            # 如果权重差异小于容忍度，则不调整
            if abs(target_weight - current_weight) > tolerance:
                rebalance_flag = True
                break
        if rebalance_flag:
            for symbol, target_weight in target_weights.items():
                
                current_weight = current_weights.get(symbol, 0.0)
                issell = target_weight < current_weight
                # 如果权重差异小于容忍度，则不调整
                if abs(target_weight - current_weight) <= tolerance:
                    continue
                    
                # 计算目标市值

                target_value = total_value * target_weight
                
                # 获取当前价格
                logger.info(f"获取当前价格 {[symbol,type(symbol)]}")
                current_fullprice = self.get_current_price(symbol,'full')
                
                # 计算目标数量
                target_quantity = int(target_value / current_fullprice)
                
                # 获取当前持仓数量
                current_quantity = current_positions[symbol].quantity \
                                        if symbol in current_positions else 0
                
                # 计算需要调整的数量
                quantity_diff = target_quantity - current_quantity
                
                if quantity_diff != 0:
                    # 发出买入或卖出订单
                    direction = 1 if quantity_diff > 0 else -1
                    self.place_order(
                        symbol=symbol,
                        order_type=OrderType.MARKET,
                        asset_type=self.available_assets[symbol].asset_type,
                        quantity=abs(quantity_diff),
                        direction=direction
                    )
                    
                
            logger.info(
                "目标仓位\n"
                f"目标权重: {target_weights}\n"
                f"当前权重: {current_weights}"
            )
        else:
            logger.info("目标仓位不需要调整")
            logger.info(f"目标权重: {target_weights}\n")
            logger.info(f"当前权重: {current_weights}")

    
    def initialize(self) -> None:
        """
        策略初始化
        用于设置策略参数、指标等
        """
        # 初始化策略
        pass
    
    def set_engine(self,engine):
        self.engine = engine
        self.market_data = engine.market_data
        self.available_assets = engine.assets
        self.portfolio = engine.position
        self.place_order = engine.place_order
    @virtual
    def on_data(self,data:BarData) -> None:
        """
        数据更新事件
        处理最新市场数据，生成交易信号
        """
        pass
    @virtual
    def on_order_fill(self, trade: TradeData) -> None:
        """
        订单成交事件
        
        参数:
            trade: 成交事件
        """
        pass
    @virtual
    def on_cashflow(self, cashflow:CashFlowData) -> None:
        """
        现金流事件
        
        参数:
            cashflow_event: 现金流事件
        """
        pass
    def on_dataset(self,dataset:Dict[str,BarData]):
        """
        处理一揽子标的市场行情，通常用在投资组合策略上
        
        参数:
            dataset: 一揽子标的同一时间的市场行情
        """
        pass
